package com.linxin.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.linxin.reggie.common.BaseContext;
import com.linxin.reggie.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 检查用户是否登录
 */

/**
 * WebFilter注解需要在启动类添加ServletComponentScan注解才能使用功能
 * filterName: 过滤器名称
 * urlPatterns: 拦截的url地址
 */
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    // 路径匹配器, 支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    /**
     * 设置过滤器
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 将ServletRequest转为HttpServletRequest
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        /**
         * 1. 获取本次请求的uri
         * 2. 判断本次请求是否需要处理
         * 3. 如果不需要处理, 则直接放行
         * 4. 判断登录状态, 如果已登录, 则直接放行
         * 5. 如果未登录, 则返回未登录结果
         */

        /**
         * <url和uri区别>
         */
        /*
        uri是url的父级，url是uri的子级。
        因为url继承了所有uri的内容，所以它比uri更加详细，但是uri是它的父级。
        url的作用
        url一般是一个完整的链接，我们可以直接通过这个链接（url）访问到一个网站，
        或者把这个url复制到浏览器访问网站。
        使用URL时我们就是一个直接用户的角色，直接访问就完事了。
        uri的作用
        uri并不是一个直接访问的链接，而是相对地址（当然如果相对于浏览器那么uri等同于url了）。
        这种概念更多的是用于编程中，因为我们没必要每次编程都用绝对url来获取一些页面，
        这样还需要进行分割“http://xx/xxx”前面那一串，
        所以编程的时候直接request.getRequestURI就行了，
        当然如果是重定向的话，就用URL。
         */

        // 1. 获取本次请求的uri
        // 输入日志, 输出拦截到的url地址
        // log.info("本次请求的uir为: {}", request.getRequestURI());
        String requestURI = request.getRequestURI();

        // 2. 判断本次请求是否需要处理
        // 设置不需要处理的地址, 需要处理的地址不是页面地址, 是请求后端的地址
        String[] uris = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**", //通配符
                "/front/**", //通配符
                "/user/login",
                "/user/sendMsg",
                "/doc.html",
                "web.jars/**",
                "/swagger-resources",
                "/v2/api-docs"
        };

        boolean check = checkPath(uris, requestURI);

        // 3. 如果不需要处理, 则直接放行
        if (check) {
            // log.info("本次不需要处理, 不需要处理的uri为{}", requestURI);
            filterChain.doFilter(request, response);
            return; // 结束方法
        }

        // 4. (后台)判断登录状态, 如果已登录, 则直接放行
        if (request.getSession().getAttribute("employee") != null) {
            // log.info("用户已登录, 用户id为: {}", request.getSession().getAttribute("employee"));

            // 获取当前用户的id
            long empID = (long) request.getSession().getAttribute("employee");
            // 将id负责给baseContext
            BaseContext.setCurrentId(empID);

            filterChain.doFilter(request, response);
            return; // 结束方法
        }

        // 4. (移动端)判断登录状态, 如果已登录, 则直接放行
        if (request.getSession().getAttribute("user") != null) {
            // log.info("用户已登录, 用户id为: {}", request.getSession().getAttribute("employee"));

            // 获取当前用户的id
            long userID = (long) request.getSession().getAttribute("user");
            // 将id负责给baseContext
            BaseContext.setCurrentId(userID);

            filterChain.doFilter(request, response);
            return; // 结束方法
        }

        // 5. 如果未登录, 则返回未登录结果
        log.info("用户未登录, 被拦截的uri为{}", requestURI);
        response.getWriter().write(JSON.toJSONString(Result.error("NOTLOGIN")));
        return;

    }

    /**
     * 判断请求的uri是否为不拦截的
     *
     * @param uris
     * @param requestUri
     * @return
     */
    public boolean checkPath(String[] uris, String requestUri) {
        // 遍历uris
        for (String uri : uris) {
            // 使用路径匹配器
            boolean match = PATH_MATCHER.match(uri, requestUri);
            if (match) { // 表示是否为不需要拦截的uri
                return true;
            }
        }
        return false; // 需要拦截的
    }
}
